
#include "ipfe_base.h"

// [*] CImagePositionFeatureExtractor

CImagePositionFeatureExtractor::~CImagePositionFeatureExtractor()
{
	this->QueryX = this->QueryY = -1;
}

void CImagePositionFeatureExtractor::SetQuery(int x, int y)
{
	this->QueryX = x;
	this->QueryY = y;
}

double CImagePositionFeatureExtractor::GetFeature(int x, int y, int FeatureIndex)
{
	SetQuery(x, y);
	return GetFeature(FeatureIndex);
}

size_t CImagePositionFeatureExtractor::GetMemoryUsage() const
{
	return sizeof(int) * 2;
}

// [*] CBasicIPFE

void CBasicIPFE::myRelease()
{
	CDataStructureTools::MyRelease_Single(pIPFC);
}

void CBasicIPFE::ClearCalculationHistory()
{
	LastX = LastY = -1;
	FeatureCache.Fill(0.0);
	FeatureFlag.Fill(false);
}

CBasicIPFE::CBasicIPFE()
{
	pIPFC = NULL;
	myInit(NULL);
}

CBasicIPFE::CBasicIPFE(CImagePositionFeatureCalculator* pInitializedIPFC)
{
	pIPFC = NULL;
	myInit(pInitializedIPFC);
}

CBasicIPFE::CBasicIPFE(const CBasicIPFE& AnotherInstance)
{
	pIPFC = NULL;
	myInit(AnotherInstance.pIPFC->myClone());
}

CBasicIPFE& CBasicIPFE::operator = (const CBasicIPFE& AnotherInstance)
{
	if (this != &AnotherInstance)
	{
		CImagePositionFeatureCalculator* tPtr = NULL;
		if (AnotherInstance.pIPFC != NULL)
			tPtr = AnotherInstance.pIPFC->myClone();
		myInit(tPtr);
	}
	return *this;
}

CBasicIPFE::~CBasicIPFE()
{
	myRelease();
}

void CBasicIPFE::myInit(CImagePositionFeatureCalculator* pInitializedIPFC)
{
	myRelease();
	pIPFC = pInitializedIPFC;
	RecomputeFeatureDimension();

	ImageHandle = 0;
}

CImagePositionFeatureExtractor* CBasicIPFE::myClone()
{
	return new CBasicIPFE(*this);
}

void CBasicIPFE::ClearContent()
{
	ClearCalculationHistory();
	ImageHandle = 0;
	if (pIPFC != NULL)
		pIPFC->ClearContent();
}

int CBasicIPFE::SetImage(const CImg<int>& imgSrcRGB, string strFN_ImageCacheHashName)
{
	if (pIPFC == NULL) return (ImageHandle = 0);

	ImageHandle = pIPFC->SetImage(imgSrcRGB, strFN_ImageCacheHashName);

	ClearCalculationHistory();

	return ImageHandle;
}

int CBasicIPFE::GetFeatureDimension()
{
	return Dim;
}

int CBasicIPFE::RecomputeFeatureDimension()
{
	if (pIPFC == NULL)
		Dim = 0;
	else
		Dim = pIPFC->RecomputeFeatureDimension();
	FeatureCache.Resize(Dim);
	FeatureFlag.Resize(Dim);

	ClearCalculationHistory();

	return Dim;
}

double CBasicIPFE::GetFeature(int FeatureIndex)
{
	if (FeatureIndex<0 || FeatureIndex>=Dim) return 0.0;
	
	if (QueryX!=LastX || QueryY!=LastY)
	{
		ClearCalculationHistory();
		LastX = QueryX;
		LastY = QueryY;
	}
	
	if (!FeatureFlag.pElement[FeatureIndex])
		pIPFC->CalcFeature(QueryX, QueryY, FeatureIndex, FeatureCache.pElement, FeatureFlag.pElement);

	return FeatureCache.pElement[FeatureIndex];
}

size_t CBasicIPFE::GetMemoryUsage() const
{
	return CImagePositionFeatureExtractor::GetMemoryUsage()
		+ sizeof(int) * 4
		+ FeatureCache.GetMemoryUsage()
		+ FeatureFlag.GetMemoryUsage()
		+ sizeof(CImagePositionFeatureCalculator*)
		+ pIPFC->GetMemoryUsage();
}

// [*] CCompositeIPFC

void CCompositeIPFC::myRelease()
{
	size_t i;
	CImagePositionFeatureCalculator** ptr;

	ptr = IPFCList.pElement;
	for ( i = 0; i < IPFCList.Size; ++i )
		CDataStructureTools::MyRelease_Single(*(ptr++));
	Clear_IPFC_List_WithoutDeletion();
}

void CCompositeIPFC::Clear_IPFC_List_WithoutDeletion()
{
	Dim = 0;
	IPFCList.Clear();
	FeatureStart.Resize(1); FeatureStart.pElement[0] = 0;
}

CCompositeIPFC::CCompositeIPFC()
{
	Clear_IPFC_List_WithoutDeletion();
}

CCompositeIPFC::CCompositeIPFC(const CCompositeIPFC& AnotherInstance)
{
	Clear_IPFC_List_WithoutDeletion();
	myInit(AnotherInstance);
}

CCompositeIPFC& CCompositeIPFC::operator = (const CCompositeIPFC& AnotherInstance)
{
	if (this != &AnotherInstance)
		myInit(AnotherInstance);
	return *this;
}

CCompositeIPFC::~CCompositeIPFC()
{
	myRelease();
}

void CCompositeIPFC::Clear_IPFC_List()
{
	myRelease();
}

void CCompositeIPFC::PushBack(CImagePositionFeatureCalculator* pInitializedIPFC)
{
	Dim += pInitializedIPFC->GetFeatureDimension();
	IPFCList.PushBack(pInitializedIPFC);
	FeatureStart.PushBack(Dim);
}

void CCompositeIPFC::myInit(const CCompositeIPFC& AnotherInstance)
{
	size_t i;

	myRelease();
	for ( i = 0; i < AnotherInstance.IPFCList.Size; ++i )
		PushBack(AnotherInstance.IPFCList.pElement[i]->myClone());
}

CImagePositionFeatureCalculator* CCompositeIPFC::myClone() const
{
	return new CCompositeIPFC(*this);
}

void CCompositeIPFC::ClearContent()
{
	ImageHandle = 0;
	size_t i;
	for ( i = 0; i < IPFCList.Size; ++i )
		IPFCList.pElement[i]->ClearContent();
}

int CCompositeIPFC::SetImage(const CImg<int>& imgSrcRGB, string strFN_ImageCacheHashName)
{
	size_t i;

	ImageHandle = CMyRandom::NextInt(2147483647)+1;

	for ( i = 0; i < IPFCList.Size; ++i )
		IPFCList.pElement[i]->SetImage(imgSrcRGB, strFN_ImageCacheHashName);

	return ImageHandle;
}

int CCompositeIPFC::GetFeatureDimension()
{
	return Dim;
}

int CCompositeIPFC::RecomputeFeatureDimension()
{
	Dim = 0;
	FeatureStart.Resize(1); FeatureStart.pElement[0] = 0;
	size_t i;
	for ( i = 0; i < IPFCList.Size; ++i )
	{
		Dim += IPFCList.pElement[i]->RecomputeFeatureDimension();
		FeatureStart.PushBack(Dim);
	}
	return Dim;
}

void CCompositeIPFC::CalcFeature(
	int x, int y, int FeatureIndex,
	double* DstData, bool* DstGotFlag
	)
{
	if (FeatureIndex<0 || FeatureIndex>=Dim) return;
	int* ii = upper_bound(FeatureStart.pElement, FeatureStart.End(), FeatureIndex) - 1;
	IPFCList.pElement[ii - FeatureStart.pElement]->CalcFeature(x, y, FeatureIndex - *ii, DstData + *ii, DstGotFlag + *ii);
}

size_t CCompositeIPFC::GetMemoryUsage() const
{
	size_t ret = sizeof(int) + IPFCList.GetMemoryUsage() + FeatureStart.GetMemoryUsage();
	int i;
	for ( i = 0; i < (int)IPFCList.Size; ++i )
		ret += IPFCList.pElement[i]->GetMemoryUsage();
	return ret;
}

// [*] C_IPFC_ImageFilterBankOwner

C_IPFC_ImageFilterBankOwner::C_IPFC_ImageFilterBankOwner()
{
	pIFB = NULL;
	InitImageFilterBank(NULL, 0, false);
}

C_IPFC_ImageFilterBankOwner::C_IPFC_ImageFilterBankOwner(CImageFilterBank* pNewIFB, int ImageExtent, bool fSharedIFB)
{
	pIFB = NULL;
	InitImageFilterBank(pNewIFB, ImageExtent, fSharedIFB);
}

C_IPFC_ImageFilterBankOwner::C_IPFC_ImageFilterBankOwner(const C_IPFC_ImageFilterBankOwner& AnotherInstance)
{
	pIFB = NULL;
	InitImageFilterBank(NULL, 0, false);
	CloneWithoutContentFrom(AnotherInstance);
}

C_IPFC_ImageFilterBankOwner& C_IPFC_ImageFilterBankOwner::operator = (const C_IPFC_ImageFilterBankOwner& AnotherInstance)
{
	if (&AnotherInstance == this) return *this;
	CloneWithoutContentFrom(AnotherInstance);
	return *this;
}

C_IPFC_ImageFilterBankOwner::~C_IPFC_ImageFilterBankOwner()
{
	if (!(this->fSharedIFB))
		CDataStructureTools::MyRelease_Single(pIFB);
	else
		pIFB = NULL;
	ClearResponseUserSet();
}

void C_IPFC_ImageFilterBankOwner::CloneWithoutContentFrom(const C_IPFC_ImageFilterBankOwner& AnotherInstance)
{
	InitImageFilterBank(AnotherInstance.pIFB->myClone(), AnotherInstance.ImageExtent, AnotherInstance.fSharedIFB);
	UserAmount = AnotherInstance.UserAmount;
	FeatureDimension = AnotherInstance.FeatureDimension;
	UserList.Clear();
	int i;
	for ( i = 0; i < AnotherInstance.UserAmount; ++i )
		UserList.PushBack(AnotherInstance.UserList.pElement[i]->myClone());
	AreUserInitializedList.Resize(UserAmount); AreUserInitializedList.Fill(false);
	UserFeatureStartList = AnotherInstance.UserFeatureStartList;
	UserInputResponseIndexList = AnotherInstance.UserInputResponseIndexList;
	UserInputResponseStartList = AnotherInstance.UserInputResponseStartList;
	ClearContent();
}

void C_IPFC_ImageFilterBankOwner::InitImageFilterBank(CImageFilterBank* pNewIFB, int ImageExtent, bool fSharedIFB)
{
	if (!(this->fSharedIFB))
		CDataStructureTools::MyRelease_Single(pIFB);
	else
		pIFB = NULL;
	this->pIFB = pNewIFB;
	this->fSharedIFB = fSharedIFB;
	this->ImageExtent = ImageExtent;
	ClearResponseUserSet();
}

void C_IPFC_ImageFilterBankOwner::ClearResponseUserSet()
{
	CDataStructureTools::ReleasePointers(UserList);
	UserAmount = 0;
	AreUserInitializedList.Clear();
	FeatureDimension = 0;
	UserFeatureStartList.Clear(); UserFeatureStartList.PushBack(0);
	InputResponseDimension = 0;
	UserInputResponseStartList.Clear(); UserInputResponseStartList.PushBack(0);
	UserInputResponseIndexList.Clear();
	ClearContent();
}

void C_IPFC_ImageFilterBankOwner::PushBackResponseUser(C_IPFC_ResponseUser* pNewUser, const int* pInputResponseIndex)
{
	++UserAmount;
	UserList.PushBack(pNewUser);
	AreUserInitializedList.PushBack(false);
	FeatureDimension += pNewUser->GetFeatureDimension();
	UserFeatureStartList.PushBack(FeatureDimension);
	int v = pNewUser->GetInputResonseDimension();
	InputResponseDimension += v;
	UserInputResponseStartList.PushBack(InputResponseDimension);
	UserInputResponseIndexList.PushBack(pInputResponseIndex, v);
}

void C_IPFC_ImageFilterBankOwner::ClearContent()
{
	AreUserInitializedList.Fill(false);
	ImageHandle = 0;
	W = H = WH = 0;
	
	if (pIFB != NULL) pIFB->ClearContent();
	int i;
	for ( i = 0; i < UserAmount; ++i )
		UserList.pElement[i]->ClearContent();
}

int C_IPFC_ImageFilterBankOwner::SetImage(const CImg<int>& imgSrcRGB, string strFN_ImageCacheHashName)
{
	ClearContent();
	if (pIFB == NULL) return (ImageHandle = 0);

	CImg<int> imgEnlargedSrcRGB;
	CExternalTools::EnlargeImage(imgEnlargedSrcRGB, imgSrcRGB, ImageExtent, ImageExtent);
 	ImageHandle = this->pIFB->Work(imgEnlargedSrcRGB, strFN_ImageCacheHashName);
	W = imgSrcRGB.width;
	H = imgSrcRGB.height;
	WH = W * H;
	AreUserInitializedList.Fill(false);
	return ImageHandle;
}

size_t C_IPFC_ImageFilterBankOwner::GetMemoryUsage() const
{
	size_t ret = sizeof(CImageFilterBank*) + sizeof(int) * 7
		+ UserList.GetMemoryUsage()
		+ AreUserInitializedList.GetMemoryUsage()
		+ UserFeatureStartList.GetMemoryUsage()
		+ UserInputResponseStartList.GetMemoryUsage()
		+ UserInputResponseIndexList.GetMemoryUsage();
	int i;
	for ( i = 0; i < UserAmount; ++i )
		ret += UserList.pElement[i]->GetMemoryUsage();
	return 0;
}

int C_IPFC_ImageFilterBankOwner::GetFeatureDimension()
{
	return FeatureDimension;
}

int C_IPFC_ImageFilterBankOwner::RecomputeFeatureDimension()
{
	FeatureDimension = 0;
	UserFeatureStartList.Clear(); UserFeatureStartList.PushBack(0);
	int i;
	for ( i = 0; i < UserAmount; ++i )
	{
		FeatureDimension += UserList.pElement[i]->RecomputeFeatureDimension();
		UserFeatureStartList.PushBack(FeatureDimension);
	}
	return FeatureDimension;
}

CImagePositionFeatureCalculator* C_IPFC_ImageFilterBankOwner::myClone() const
{
	return new C_IPFC_ImageFilterBankOwner(*this);
}

void C_IPFC_ImageFilterBankOwner::CalcFeature(
	int x, int y, int FeatureIndex,
	double* DstData, bool* DstGotFlag)
{
	if (FeatureIndex<0 || FeatureIndex>=FeatureDimension) return;

	int iu = (int)(upper_bound(UserFeatureStartList.Begin(), UserFeatureStartList.End(), FeatureIndex) - UserFeatureStartList.Begin() - 1);
	C_IPFC_ResponseUser* pUser = UserList.pElement[iu];

	if (!AreUserInitializedList.pElement[iu])
	{
		AreUserInitializedList.pElement[iu] = true;

		int V = pUser->GetInputResonseDimension();
		int WHV = WH * V;
		const int* qri = UserInputResponseIndexList.pElement + UserInputResponseStartList.pElement[iu];
		double* pData = new double[WHV];
		const double* qData;
		double* pd;
		const double* qd;
		int i, jx, jy;

		//CImg<double> imgA;
		//CImg<int> imgOutput;

		pd = pData;
		for ( i = 0; i < V; ++i )
		{
			qData = pIFB->GetOutput(*(qri++));

			//imgA.assign(qData + (W+ImageExtent+ImageExtent)*(H+ImageExtent+ImageExtent) * i, W+ImageExtent+ImageExtent, H+ImageExtent+ImageExtent, 1, 1, true);
			//CExternalTools::NormalizeToGeneralIntGrayscaleImage(imgA, imgOutput);
			//imgOutput.display();

			for ( jy = 0; jy < H; ++jy )
			{
				qd = qData + (W + ImageExtent + ImageExtent) * (jy + ImageExtent) + ImageExtent;
				for ( jx = 0; jx < W; ++jx )
					*(pd++) = *(qd++);
			}

			//imgA.assign(pData + WH * i, W, H, 1, 1, true);
			//CExternalTools::NormalizeToGeneralIntGrayscaleImage(imgA, imgOutput);
			//imgOutput.display();
		}

		pUser->SetInputResponse(pData, W, H);

		delete[] pData;
	}

	int ir = UserFeatureStartList.pElement[iu];
	pUser->ComputeFeature(x, y, FeatureIndex - ir, DstData + ir, DstGotFlag + ir);
}

////// [*] CFilterResponseIPFC
////
////void CFilterResponseIPFC::myRelease()
////{
////	pImageFilterBank = NULL;
////	InputResponseList.Clear();
////	ClearResponseData();
////}
////
////CFilterResponseIPFC::CFilterResponseIPFC()
////{
////	CSimpleTypeArray<int> a;
////	a.Clear();
////	InitResponseInputSettings(NULL, 0, a);
////}
////
////CFilterResponseIPFC::CFilterResponseIPFC(CImageFilterBank* pFixedImageFilterBank, int ImageExtent, const CSimpleTypeArray<int>& InputResponseList)
////{
////	InitResponseInputSettings(pFixedImageFilterBank, ImageExtent, InputResponseList);
////}
////
////CFilterResponseIPFC::CFilterResponseIPFC(const CFilterResponseIPFC& AnotherInstance)
////{
////	InitResponseInputSettings(AnotherInstance.pImageFilterBank, AnotherInstance.ImageExtent, AnotherInstance.InputResponseList);
////}
////
////void CFilterResponseIPFC::InitResponseInputSettings(CImageFilterBank* pFixedImageFilterBank, int ImageExtent, const CSimpleTypeArray<int>& InputResponseList)
////{
////	this->pImageFilterBank = pFixedImageFilterBank;
////	this->ImageExtent = (ImageExtent<0 ? 0 : ImageExtent);
////	this->InputResponseList = InputResponseList;
////	ClearResponseData();
////	V = (int)InputResponseList.Size;
////}
////
////CFilterResponseIPFC::~CFilterResponseIPFC()
////{
////	myRelease();
////}
////
////void CFilterResponseIPFC::ClearResponseData()
////{
////	ImageHandle = 0;
////	WHV = WH = W = H = V = 0;
////}
////
////int CFilterResponseIPFC::SetImage(const CImg<int>& imgSrcRGB, string strFN_ImageCacheHashName)
////{
////	if (pImageFilterBank == NULL) return (ImageHandle = 0);
////
////	CImg<int> imgEnlargedSrcRGB;
////	CExternalTools::EnlargeImage(imgEnlargedSrcRGB, imgSrcRGB, ImageExtent, ImageExtent);
////
////	int iv,iy,ix;
////	double* pr;
////	const double* qr;
////
////	W = imgSrcRGB.width;
////	H = imgSrcRGB.height;
////	WH = W * H; WHV = WH * V;
////
////	size_t ww = imgEnlargedSrcRGB.width;
////	size_t hh = imgEnlargedSrcRGB.height;
////	size_t wwhh = ww * hh;
////	size_t wwhhvv = wwhh * V;
////
////	ImageHandle = pImageFilterBank->Work(imgEnlargedSrcRGB, strFN_ImageCacheHashName);
////
////	double* re = new double [WHV];
////	pr = re; for ( ix = 0; ix < (int)WHV; ++ix ) *(pr++) = 0.0;
////
////	pr = re;
////	for ( iv = 0; iv < V; ++iv ) for ( iy = 0; iy < H; ++iy )
////	{
////		qr = pImageFilterBank->GetOutput(InputResponseList.pElement[iv]) + 
////			+ ((size_t)(iy + ImageExtent)) * ww
////			+ (size_t)ImageExtent;
////		for ( ix = 0; ix < W; ++ix )
////			*(pr++) = *(qr++);
////	}
////
////	//SPACE CONTROLABLE
////
////	//pImageFilterBank->OutputList.Clear();
////	pImageFilterBank->OutputList.DeepClear();
////
////	SetResponseImage(re);
////
////	delete[] re;
////
////	return ImageHandle;
////}
////
////size_t CFilterResponseIPFC::GetMemoryUsage() const
////{
////	return sizeof(int) * 4 + sizeof(size_t) * 2 + sizeof(CImageFilterBank*) + InputResponseList.GetMemoryUsage();
////}
////
